/*
 * (C) 2009, Elephant Seven Hamburg Gmbh, 
 * Author: Detlev Wiese
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
 * use this file except in compliance with the License. You may obtain a copy 
 * of the License at
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.se and its accompanying notice file is
 * included with the distribution. 
 */ 
package com.e7.streams;

import java.util.Arrays;
import java.util.Collection;
import java.util.NoSuchElementException;

/**
 * <P>This class implements the concatenation of streams of the 
 * same element type.</P>
 * <P>Concatenation means: the first stream is read first and its 
 * elements are delivered as elements of the ConcatenatedStream-instance. 
 * After the first stream all elements of the second stream are delivered 
 * and so on.</P>   
 * @param <E> The type of the elements of the streams concatenated
 */
public class ConcatenatedStream<E> extends AbstractStream<E>
	implements Stream<E> 
{
	private Stream<? extends Stream<E>> streamOfStreams = null; 
	private Stream<E> currentStream = null; 
	
	/**
	 * Create an instance!
	 * @param s The stream of streams to concatenate
	 */
	public ConcatenatedStream(Stream<? extends Stream<E>> s) {
		streamOfStreams = s;
	}

	/**
	 * <P>Create an instance!</P>
	 * <P>The order of the stream processing will depend on the order 
	 * defined by the default iterator of the collection.</P>
	 * @param c The collection of streams to concatenate
	 */
	public ConcatenatedStream(Collection<Stream<E>> c) {
		streamOfStreams = new CollectionStream<Stream<E>>(c);
	}
	
	/**
	 * Create an instance!
	 * @param streams The stream of streams to concatenate
	 */
	public ConcatenatedStream(Stream<E>[] s) {
		this(Arrays.asList(s));
	}
	
	public boolean hasNext() {
		if (null == currentStream) { 
			if (streamOfStreams.hasNext()) 
				currentStream = streamOfStreams.next();
			else 
				return false; 
		}
		
		if (currentStream.hasNext())
			return true;
		else {
			while (streamOfStreams.hasNext()) {
				currentStream = streamOfStreams.next(); 
				if (currentStream.hasNext()) 
					return true; 
			}
			return false;
		}
	}

	public E next() {
		if (this.hasNext())
			return currentStream.next();
		else 
			throw new NoSuchElementException(); 
	}
}
